package com.wchen.game.client;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * @Author: wchen
 * @Description:
 * @Date: Created in 2019-04-19 09:46
 * Copyright 本内容仅限于杭州阿拉丁信息科技股份有限公司内部传阅，禁止外泄以及用于其他的商业目的
 */
public class NioGameClient {
    private final static Logger logger = LoggerFactory.getLogger(NioGameClient.class);

    private SocketChannel socketChannel;
    private Selector selector;
    private InetSocketAddress socketAddress;
    private volatile Boolean stop = false;

    public static void main(String[] args) throws Exception {
//        NioGameClient gameClient = new NioGameClient();
//        gameClient.startClient("127.0.0.1", 8234);
//        Thread.sleep(1000 * 60 * 60);
        ByteBuffer byteBuffer = ByteBuffer.allocate(18);
        byteBuffer.put("1234567890abcdefg".getBytes());
        int remain = byteBuffer.remaining();
        byteBuffer.flip();
        remain = byteBuffer.remaining();
        byteBuffer.position(13);
        byteBuffer.put("--------".getBytes());

        byteBuffer.flip();
        byte [] readBytes = new byte[byteBuffer.remaining()];
        byteBuffer.get(readBytes);
        System.out.println(new String(readBytes, "UTF-8"));

//
//        byteBuffer.mark();
//        byteBuffer.position(10);
//        byteBuffer.mark();
//        byteBuffer.position(5);
//        byteBuffer.reset();
        System.out.println(byteBuffer.toString());

    }

    private void init(String ip, Integer port){
        try {
            socketAddress = new InetSocketAddress(ip, port);
            socketChannel = SocketChannel.open();
            socketChannel.configureBlocking(false);
            selector = Selector.open();
            socketChannel.register(selector, SelectionKey.OP_CONNECT);
        }catch (IOException e){
            logger.error("client init error ", e);
        }
    }

    public void startClient(String ip, Integer port){

        init(ip, port);
        try {
            if (socketChannel.connect(new InetSocketAddress("127.0.0.1", port))) {
                logger.info("startClient connect .");
                socketChannel.register(selector, SelectionKey.OP_READ);
                writeResponse(socketChannel, "TMD.......................");
            } else {
                logger.info("startClient isNotConnected ...");
                socketChannel.register(selector, SelectionKey.OP_CONNECT);
            }

//            if (socketChannel.connect(new InetSocketAddress(8234))) {
//                logger.info("startClient connect .");
//                if (socketChannel.finishConnect()) {
//                    logger.info("startClient finishConnect .");
//                    socketChannel.register(selector, SelectionKey.OP_CONNECT);
//                }
//            }else{
//                logger.info("startClient isNotConnected ...");
//                socketChannel.register(selector, SelectionKey.OP_CONNECT);
//            }
        }catch (IOException e){
            logger.error("startClient error ", e);
        }

        new Thread(() -> runSelector()).start();


    }

    private void runSelector() {
        logger.info("runSelector begin ...");
        while (!stop) {
            try {
                selector.select(1000);
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {

                    SelectionKey selectionKey = iterator.next();
                    iterator.remove();
                    try {
                        handleEvent(selectionKey);
                    } catch (Exception e) {
                        logger.error("handleEvent error ", e);
                        if (selectionKey != null) {
                            selectionKey.cancel();
                            if (selectionKey.channel() != null) {
                                selectionKey.channel().close();
                            }
                            selector.selectNow();
                        }
                    }
                }
            } catch (IOException e) {
                logger.error("runSelector error ", e);
            }
        }
    }


    /**
     * 处理各个阶段事件
     * @param selectionKey
     * @throws IOException
     */
    private void handleEvent(SelectionKey selectionKey) throws IOException {

        if(!selectionKey.isValid()){
            logger.info("handleEvent selectionKey is unValid!");
            return ;
        }

        SocketChannel sc = (SocketChannel)selectionKey.channel();
        if(selectionKey.isConnectable()){
            logger.info("SelectionKey isConnectable ...");
            if(sc.finishConnect()){
                logger.info("SelectionKey finishConnect ...");
                sc.register(selector, SelectionKey.OP_READ);
                //写入消息
                writeResponse(sc, "is my request!!!");
            }else{
                logger.info("SelectionKey isNotConnectable");
            }

        }

        if(selectionKey.isReadable()){
            logger.info("SelectionKey current isReadable ...");
            ByteBuffer readBuffer = ByteBuffer.allocate(1024);
            int readResult = sc.read(readBuffer);
            if(readResult > 0) {
                readBuffer.flip();
                byte[] readBytes = new byte[readBuffer.remaining()];
                readBuffer.get(readBytes);
                String readMessage = new String(readBytes, "UTF-8");
                logger.info("********************************************************************************");
                logger.info("**********************************{}****************************", readMessage);
                logger.info("********************************************************************************");
                String responseMsg = new StringBuilder().append("current time is [").append(System.currentTimeMillis()).append("]").toString();
                writeResponse(sc, responseMsg);
            }else if(readResult < 0){
                selectionKey.cancel();
                sc.close();
            }else {
                //...
            }
        }


    }

    /**
     * 写入应答消息
     * @param socketChannel
     * @param responseMsg
     * @throws IOException
     */
    private void writeResponse(SocketChannel socketChannel, String responseMsg) throws IOException {
        byte[] writeBytes = responseMsg.getBytes();
        ByteBuffer writeBuffer = ByteBuffer.allocate(writeBytes.length);
        writeBuffer.put(writeBytes);
        writeBuffer.flip();
        int result = socketChannel.write(writeBuffer);
        logger.info("writeResponse result {}", result);
    }

}
